/*
 * GDevelop C++ Platform
 * Copyright 2008-2016 Florian Rival (Florian.Rival@gmail.com). All rights
 * reserved. This project is released under the MIT License.
 */
#include "GDCpp/Runtime/SoundManager.h"
#include <iostream>
#include <string>
#include <vector>
#include "GDCpp/Runtime/Music.h"
#include "GDCpp/Runtime/Project/ResourcesManager.h"
#include "GDCpp/Runtime/ResourcesLoader.h"
#include "GDCpp/Runtime/Sound.h"
#include "GDCpp/Runtime/String.h"

SoundManager::SoundManager() : globalVolume(100), resourcesManager(nullptr) {}

const gd::String& SoundManager::GetFileFromSoundName(
    const gd::String& name) const {
  if (!resourcesManager || !resourcesManager->HasResource(name)) return name;

  return resourcesManager->GetResource(name).GetFile();
}

void SoundManager::PlaySoundOnChannel(const gd::String& name,
                                      unsigned int channel,
                                      bool repeat,
                                      float volume,
                                      float pitch) {
  std::shared_ptr<Sound> sound =
      std::make_shared<Sound>(GetFileFromSoundName(name));
  sound->sound.play();
  sound->sound.setRelativeToListener(true);

  SetSoundOnChannel(channel, sound);
  GetSoundOnChannel(channel)->sound.setLoop(repeat);
  GetSoundOnChannel(channel)->SetVolume(volume, globalVolume);
  GetSoundOnChannel(channel)->SetPitch(pitch);
}

void SoundManager::PlaySound(const gd::String& name,
                             bool repeat,
                             float volume,
                             float pitch) {
  sounds.push_back(
      std::shared_ptr<Sound>(new Sound(GetFileFromSoundName(name))));
  sounds.back()->sound.play();
  sounds.back()->sound.setRelativeToListener(true);

  sounds.back()->sound.setLoop(repeat);
  sounds.back()->SetVolume(volume, globalVolume);
  sounds.back()->SetPitch(pitch);
}

void SoundManager::PlayMusic(const gd::String& name,
                             bool repeat,
                             float volume,
                             float pitch) {
  const gd::String& file = GetFileFromSoundName(name);
  std::shared_ptr<Music> music(new Music);
#if !defined(GD_IDE_ONLY)
  gd::ResourcesLoader* ressourcesLoader = gd::ResourcesLoader::Get();
  if (ressourcesLoader->HasFile(file)) {
    std::size_t size = ressourcesLoader->GetBinaryFileSize(file);
    music->SetBuffer(ressourcesLoader->LoadBinaryFile(file), size);
    music->OpenFromMemory(size);
  } else
#endif
  {
    music->OpenFromFile(file);
  }

  musics.push_back(music);
  musics.back()->music.setRelativeToListener(true);
  musics.back()->Play();

  music->SetLoop(repeat);
  music->SetVolume(volume, globalVolume);
  music->SetPitch(pitch);
}

void SoundManager::PlayMusicOnChannel(const gd::String& name,
                                      unsigned int channel,
                                      bool repeat,
                                      float volume,
                                      float pitch) {
  const gd::String& file = GetFileFromSoundName(name);
  std::shared_ptr<Music> music(new Music);
#if !defined(GD_IDE_ONLY)
  gd::ResourcesLoader* ressourcesLoader = gd::ResourcesLoader::Get();
  if (ressourcesLoader->HasFile(file)) {
    std::size_t size = ressourcesLoader->GetBinaryFileSize(file);
    music->SetBuffer(ressourcesLoader->LoadBinaryFile(file), size);
    music->OpenFromMemory(size);
  } else
#endif
  {
    music->OpenFromFile(file);
  }

  SetMusicOnChannel(channel, music);
  music->music.setRelativeToListener(true);
  music->Play();

  music->SetLoop(repeat);
  music->SetVolume(volume, globalVolume);
  music->SetPitch(pitch);
}

void SoundManager::ManageGarbage() {
  for (std::size_t i = 0; i < sounds.size(); i++) {
    if (sounds[i]->sound.getStatus() == sf::Sound::Stopped)
      sounds.erase(sounds.begin() + i);
  }

  for (std::size_t i = 0; i < musics.size(); i++) {
    if (musics[i]->GetStatus() == sf::Music::Stopped)
      musics.erase(musics.begin() + i);
  }
}

std::shared_ptr<Music>& SoundManager::GetMusicOnChannel(int channel) {
  return musicsChannel[channel];
}

void SoundManager::SetMusicOnChannel(int channel,
                                     std::shared_ptr<Music> music) {
  musicsChannel[channel] = music;
}

void SoundManager::SetSoundOnChannel(int channel,
                                     std::shared_ptr<Sound> sound) {
  soundsChannel[channel] = sound;
}

std::shared_ptr<Sound>& SoundManager::GetSoundOnChannel(int channel) {
  return soundsChannel[channel];
}

void SoundManager::SetGlobalVolume(float volume) {
  globalVolume = volume;

  if (globalVolume < 0.0) globalVolume = 0.0;

  if (globalVolume > 100.0) globalVolume = 100.0;

  for (std::map<std::size_t, std::shared_ptr<Sound> >::iterator it =
           soundsChannel.begin();
       it != soundsChannel.end();
       ++it) {
    if (it->second != std::shared_ptr<Sound>())
      (it->second)->UpdateVolume(globalVolume);
  }
  for (std::map<std::size_t, std::shared_ptr<Music> >::iterator it =
           musicsChannel.begin();
       it != musicsChannel.end();
       ++it) {
    if (it->second != std::shared_ptr<Music>())
      it->second->UpdateVolume(globalVolume);
  }
  for (std::size_t i = 0; i < sounds.size(); ++i) {
    if (sounds[i] != std::shared_ptr<Sound>())
      sounds[i]->UpdateVolume(globalVolume);
  }
  for (std::size_t i = 0; i < musics.size(); ++i) {
    if (musics[i] != std::shared_ptr<Music>())
      musics[i]->UpdateVolume(globalVolume);
  }
}
